package rss_CODE;

import java.io.File;
import java.util.List;
import java.awt.Component;
import java.io.IOException;
import javax.persistence.EntityManager;

import java.util.Date;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.ArrayList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Node;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import javax.swing.JFileChooser;
import javax.swing.filechooser.FileFilter;

import rss_POJO.*;

/**
 *
 * @author RS-team
 */
public class ExportImport {
    
    private final RadioStationSystem rss;
    private final EntityManager em;

    public ExportImport(RadioStationSystem rss) {
        this.em = rss.getEM();
        this.rss = rss;
    }
  
    private List<PlayList> readPlayList(Component parent) {
        try {
            // dynamic jpql query για τη δημιουργία λίστας όλων των τραγουδιών
            return em.createQuery("SELECT pl FROM PlayList pl", PlayList.class).getResultList();
        } catch(Exception e) {
            // δεν επιστρέφω τίποτα διότι υπήρξε γενικότερο σφάλμα
            System.out.println("σφάλμα κατά την ανάγνωση PlayList");
            rss.criticalErrorBox(parent, "σφάλμα κατά την ανάγνωση PlayList");
            return null;
        } 
    }
    private List<Song> readSongList(Component parent, PlayList pl) {
        try {
            // dynamic jpql query για τη δημιουργία λίστας όλων των τραγουδιών
            return em.createNativeQuery("SELECT s.* FROM Song s, PlayList_Song pls WHERE pls.PLAYLIST_ID = " + pl.getPlaylistid().toString() + " and s.SONGID = pls.SONG_ID", Song.class).getResultList();
        } catch(Exception e) {
            // δεν επιστρέφω τίποτα διότι υπήρξε γενικότερο σφάλμα
            System.out.println("σφάλμα κατά την ανάγνωση SONGs");
            rss.criticalErrorBox(parent, "σφάλμα κατά την ανάγνωση SONGs");
            return null;
        } 
    }
    private Album readAlbum(Component parent, int id) {
        try {
            return (Album)em.createNativeQuery("SELECT a.* FROM Album a WHERE a.ALBUMID = " + Integer.toString(id), Album.class).getSingleResult();
        } catch(Exception e) {
            System.out.println("σφάλμα κατά την ανάγνωση Album");
            rss.criticalErrorBox(parent, "σφάλμα κατά την ανάγνωση Album");
            return null;
        } 
    }


    /* μέθοδος για την εξαγωγή XML */
    public void SaveXML(Component parentComponent) { 
        
        // θα δημιουργήσω ένα αρχείο όπου θα αποθηκεύσω όλα τα τραγούδια (Song) που είναι στη βάση.
        try {
            // δημιουργώ ένα καινούργιο αρχείο
            DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); // Δημιουργία parser που παράγει το DOM XML αρχείων
            DocumentBuilder docBuilder = docFactory.newDocumentBuilder(); // Δυνατότητα δημιουργίας των στιγμιότυπων του XML αρχείου από το DOM
            Document doc = docBuilder.newDocument(); // αναφέρεται στο XML αρχείο, είναι η ιδεατή ρίζα του δέντρου

            // δημιουργώ την ρίζα PlayList
            Element rootElement = doc.createElement("PlayListGroup"); // δημιουργία tag PlayListGroup
            doc.appendChild(rootElement); // προσθήκη tag ως παιδί του κόμβου rootElement

            // ανακτώ από τη βάση σε μια λίστα όλα τα αντικείμενα PlayList και τα αποθηκεύω στη μεταβλητή PlayListResults. Χρησιμοποιώ την DBManager για έλεγχο.
            List<PlayList> PlayListResults = readPlayList(parentComponent);
            // στη συνέχεια προσπελάζω τη λίστα με τα αντικείμενα PlayList
            for(PlayList pl : PlayListResults) {
                Element plelem = doc.createElement("PlayList"); // δημιουργώ ένα στοιχείο με το όνομα PlayList
                rootElement.appendChild(plelem); // τον συνδέω με τον τρέχοντα κόμβο
                
                Attr plID = doc.createAttribute("id"); // ο κόμβος αυτός έχει το id
                plID.setValue(Integer.toString(pl.getPlaylistid()));
                plelem.setAttributeNode(plID);
                
                Element plName = doc.createElement("name");
                plName.appendChild(doc.createTextNode(pl.getName())); // ο κόμβος αυτός έχει το name
                plelem.appendChild(plName);
                Element plDate = doc.createElement("creationdate");
                plDate.appendChild(doc.createTextNode(pl.getCreationdate().toString())); // ο κόμβος αυτός έχει το creationdate
                plelem.appendChild(plDate);
                
                Element SongGroup = doc.createElement("SongGroup"); // δημιουργία tag SongGroup
                plelem.appendChild(SongGroup); // προσθήκη tag ως παιδί του κόμβου rootElement
                
                // ανακτώ από τη βάση σε μια λίστα όλα τα αντικείμενα Song βάση του playlist_song και τα αποθηκεύω στη μεταβλητή results. Χρησιμοποιώ την DBManager για έλεγχο.
                List<Song> results = readSongList(parentComponent, pl);
                // στη συνέχεια προσπελάζω τη λίστα με τα αντικείμενα Song και για κάθε αντικείμενο δημιουργώ ένα καινούργιο κόμβο στο xml δέντρο που έχει τα στοιχεία του κάθε τραγουδιού που είναι αποθηκευμένο στη βάση.
                for(Song so : results) {
                    //System.out.println(so.getTitle());
                    // δημιουργώ ένα στοιχείο με το όνομα song και προσθέτω τα στοιχεία του ως κόμβους
                    Element soelem = doc.createElement("song"); 
                    SongGroup.appendChild(soelem); 
                    Attr attr = doc.createAttribute("id"); 
                    attr.setValue(Integer.toString(so.getSongid())); 
                    soelem.setAttributeNode(attr);
                    Element sotitle = doc.createElement("title");
                    sotitle.appendChild(doc.createTextNode(so.getTitle()));
                    soelem.appendChild(sotitle);
                    Element soDuration = doc.createElement("duration");
                    soDuration.appendChild(doc.createTextNode(so.getDuration().toString()));
                    soelem.appendChild(soDuration);
                    Element soTracknr = doc.createElement("tracknr");
                    soTracknr.appendChild(doc.createTextNode(so.getTracknr().toString()));
                    soelem.appendChild(soTracknr);
                    
                    // δημιουργώ ένα στοιχείο με το όνομα album και προσθέτω τα στοιχεία του ως κόμβους
                    Album alb = so.getAlbumId();
                    Element albelem = doc.createElement("album"); 
                    soelem.appendChild(albelem); 
                    Attr albAttr = doc.createAttribute("id"); 
                    albAttr.setValue(alb.getAlbumid().toString()); 
                    albelem.setAttributeNode(albAttr);
                    Element albTitle = doc.createElement("title");
                    albTitle.appendChild(doc.createTextNode(alb.getTitle()));
                    albelem.appendChild(albTitle);
                    Element albType = doc.createElement("type");
                    albType.appendChild(doc.createTextNode(alb.getType()));
                    albelem.appendChild(albType);
                    Element albDiskNumber = doc.createElement("disknumber");
                    albDiskNumber.appendChild(doc.createTextNode(alb.getDisknumber().toString()));
                    albelem.appendChild(albDiskNumber);
                    
                    // δημιουργώ ένα στοιχείο με το όνομα company και προσθέτω τα στοιχεία του ως κόμβους
                    MusicProductionCompany comp = alb.getMpcompanyId();
                    Element compelem = doc.createElement("company"); 
                    albelem.appendChild(compelem); 
                    Attr compAttr = doc.createAttribute("id"); 
                    compAttr.setValue(comp.getMpcompanyid().toString()); 
                    compelem.setAttributeNode(compAttr);
                    Element compName = doc.createElement("name");
                    compName.appendChild(doc.createTextNode(comp.getName()));
                    compelem.appendChild(compName);
                    
                    if (alb.getMusicgroupId()==null){
                        // δημιουργώ ένα στοιχείο με το όνομα artist και προσθέτω τα στοιχεία του ως κόμβους
                        Artist art = alb.getArtistId();
                        Element artelem = doc.createElement("artist"); 
                        albelem.appendChild(artelem); 
                        Attr artAttr = doc.createAttribute("id"); 
                        artAttr.setValue(art.getArtistid().toString()); 
                        artelem.setAttributeNode(artAttr);
                        
                        Element artName = doc.createElement("name");
                        artName.appendChild(doc.createTextNode(art.getArtisticname()));
                        artelem.appendChild(artName);
                        
                        // δημιουργώ ένα στοιχείο με το όνομα musicgenre και προσθέτω τα στοιχεία του ως κόμβους
                        MusicGenre mg = art.getMusicgenreId();
                        Element mgelem = doc.createElement("musicgenre"); 
                        artelem.appendChild(mgelem); 
                        Attr mgAttr = doc.createAttribute("id"); 
                        mgAttr.setValue(mg.getMusicgenreid().toString()); 
                        artelem.setAttributeNode(mgAttr);

                        Element mgName = doc.createElement("name");
                        mgName.appendChild(doc.createTextNode(mg.getName()));
                        mgelem.appendChild(mgName);
                    } else {
                        // δημιουργώ ένα στοιχείο με το όνομα musicgroup και προσθέτω τα στοιχεία του ως κόμβους
                        MusicGroup mgrp = alb.getMusicgroupId();
                        Element mgrpelem = doc.createElement("musicgroup"); 
                        albelem.appendChild(mgrpelem); 
                        Attr mgrpAttr = doc.createAttribute("id"); 
                        mgrpAttr.setValue(mgrp.getMusicgroupid().toString()); 
                        mgrpelem.setAttributeNode(mgrpAttr);
                        
                        Element mgrpName = doc.createElement("name");
                        mgrpName.appendChild(doc.createTextNode(mgrp.getName()));
                        mgrpelem.appendChild(mgrpName);
                    }
        
                    /*Album a1 = readAlbum(so.getAlbumId());*/
                    System.out.println(alb.getTitle());

                    //soelem.appendChild(name);
                }
            }
            


            // γράφω όλα τα παραπάνω σε ένα αρχείο xml
            TransformerFactory transformerFactory = TransformerFactory.newInstance();
            Transformer transformer = transformerFactory.newTransformer();
            DOMSource source = new DOMSource(doc);
            StreamResult result;

            //δημιουργώ το αρχείο xml
            JFileChooser fc = new JFileChooser();
            //για να εμφανίζει μόνο τους folders και τα xml κάνω override την accept και την getDescription
            FileFilter filter1 = new FileFilter(){
                                                    @Override
                                                    public boolean accept(File f) {
                                                        String fName = f.getName().toUpperCase();
                                                        if (fName.endsWith(".XML") || f.isDirectory()) {
                                                            return true;
                                                        } else {
                                                            return false;   
                                                        }
                                                    }

                                                    public String getDescription() {
                                                        return "Xml File (*.xml)";
                                                    }

                                                };
            fc.setFileFilter(filter1);
            int returnVal = fc.showSaveDialog(parentComponent);
            //int returnVal = fc.showOpenDialog(parentComponent);
            if (returnVal == JFileChooser.APPROVE_OPTION) {
                File fl = fc.getSelectedFile();
                String fName = fl.getName().toUpperCase();
                // αν ο χρήστης έχει δώσει όνομα χωρίς xml στην κατάληξη, του το προσθέτω
                if (fName.endsWith(".XML")) {
                    result = new StreamResult(fl);
                }
                else {
                    result = new StreamResult(new File(fl.getAbsoluteFile() + ".xml"));
                }
                transformer.transform(source, result);
                System.out.println("File saved!");
            } 
        } catch(ParserConfigurationException | TransformerException ex) {
            ex.printStackTrace();
        }  
    
    }

    
    // μέθοδοι για να ελέγχουμε αν υπάρχει ήδη το αντικείμενο στην βάση και δημιουργία αν δεν υπάρχει
    private MusicProductionCompany CreateCompany(Component parent, String name) {
        try {
            //αν υπάρχει επέστρεψέ το
            MusicProductionCompany MPCompany = (MusicProductionCompany)em.createNativeQuery("SELECT a.* FROM MusicProductionCompany a WHERE a.Name = '" + name+"'", MusicProductionCompany.class).getSingleResult();
            return MPCompany;
        } catch(Exception e) {
            //αν δεν υπάρχει δημιούργησε
            MusicProductionCompany MPCompanyCreate = new MusicProductionCompany(name);
            rss.accessDB(parent, MPCompanyCreate, "W");
            return MPCompanyCreate;
        } 
    }
    private Artist CreateArtistMusicGenre(Component parent, String artName, String mgName) {
        MusicGenre mg;
        try {
            //αν υπάρχει επέστρεψέ το
            MusicGenre mgSelect = (MusicGenre)em.createNativeQuery("SELECT a.* FROM MusicGenre a WHERE a.Name = '" + mgName + "'", MusicGenre.class).getSingleResult();
            mg = mgSelect;
        } catch(Exception e) {
            //αν δεν υπάρχει δημιούργησε
            MusicGenre mgCreate = new MusicGenre(mgName);
            rss.accessDB(parent, mgCreate, "W");
            mg = mgCreate;
        } 
        try {
            //αν υπάρχει επέστρεψέ το
            Artist a = (Artist)em.createNativeQuery("SELECT a.* FROM Artist a WHERE a.ARTISTICNAME = '" + artName + "'", Artist.class).getSingleResult();
            a.setMusicgenreId(mg);
            return a;
        } catch(Exception e) {
            //αν δεν υπάρχει δημιούργησε
            Artist aCreate = new Artist(artName);
            aCreate.setMusicgenreId(mg);
            rss.accessDB(parent, aCreate, "W");
            return aCreate;
        } 
    }
    private MusicGroup CreateMusicGroup(Component parent, String name) {
        try {
            //αν υπάρχει επέστρεψέ το
            MusicGroup mg = (MusicGroup)em.createNativeQuery("SELECT a.* FROM MusicGroup a WHERE a.Name = '" + name+"'", MusicGroup.class).getSingleResult();
            return mg;
        } catch(Exception e) {
            //αν δεν υπάρχει δημιούργησε
            MusicGroup mgCreate = new MusicGroup(name);
            rss.accessDB(parent, mgCreate, "W");
            return mgCreate;
        } 
    }
    private Album CreateAlbum(Component parent, String name, String atype, String disknumber, MusicProductionCompany mpc, MusicGroup mg, Artist art) {
        try {
            //αν υπάρχει επέστρεψέ το
            Album a = (Album)em.createNativeQuery("SELECT a.* FROM Album a WHERE a.Title = '" + name+"'", Album.class).getSingleResult();
            return a;
        } catch(Exception e) {
            //αν δεν υπάρχει δημιούργησε
            Album aCreate = new Album(name,atype,Integer.parseInt(disknumber));
            aCreate.setMpcompanyId(mpc);
            if(mg!=null){aCreate.setMusicgroupId(mg);}
            if(art!=null){aCreate.setArtistId(art);}
            rss.accessDB(parent, aCreate,"W");
            return aCreate;
        } 
    }
    private Song CreateSong(Component parent, String name, String Duration, String TrackNr, Album a) {
        try {
            //αν υπάρχει επέστρεψέ το
            Song s = (Song)em.createNativeQuery("SELECT a.* FROM Song a WHERE a.Title = '" + name+"'", Song.class).getSingleResult();
            return s;
        } catch(Exception e) {
            //αν δεν υπάρχει δημιούργησε
            Song sCreate = new Song(name,Integer.parseInt(Duration),Integer.parseInt(TrackNr));
            sCreate.setAlbumId(a); 
            rss.accessDB(parent, sCreate,"W");
            return sCreate;
        } 
    }
    private void CreatePlayList(Component parent, PlayList pl) {
        try {
            //αν υπάρχει κάνε update τις νέες τιμές.
            PlayList plSelect = (PlayList)em.createNativeQuery("SELECT a.* FROM PlayList a WHERE a.Name = '" + pl.getName() + "'", PlayList.class).getSingleResult();
            plSelect.setSongCollection(pl.getSongCollection());
            rss.accessDB(parent, plSelect,"U");
        } catch(Exception e) {
            //αν δεν υπάρχει δημιούργησε
            rss.accessDB(parent, pl,"W");
        } 
    }
    

    /* μέθοδος για την εισαγωγή XML */
    public void ImportXML (Component parentComponent) {
        JFileChooser fc = new JFileChooser();
        //για να εμφανίζει μόνο τους folders και τα xml κάνω override την accept και την getDescription
        FileFilter filter1 = new FileFilter(){
                                                @Override
                                                public boolean accept(File f) {
                                                    String fName = f.getName().toUpperCase();
                                                    if (fName.endsWith(".XML") || f.isDirectory()) {
                                                        return true;
                                                    } else {
                                                        return false;   
                                                    }
                                                }

                                                public String getDescription() {
                                                    return "Xml File (*.xml)";
                                                }

                                            };
        fc.setFileFilter(filter1);
        int returnVal = fc.showOpenDialog(parentComponent);
        if (returnVal == JFileChooser.APPROVE_OPTION) {
            File xmlFile = fc.getSelectedFile();
            DateFormat format = new SimpleDateFormat("yyyy-mm-dd");

            try {
                DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
                DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
                Document document = documentBuilder.parse(xmlFile);

                // για όλες τις PlayList
                NodeList list = document.getElementsByTagName("PlayList");
                for(int i=0; i<list.getLength(); i++) {
                    Node node = list.item(i);

                    if (node.getNodeType() == Node.ELEMENT_NODE) {
                        Element ePlayList = (Element) node;
                        Date dt = new Date();
                        try {
                            dt = format.parse(ePlayList.getElementsByTagName("creationdate").item(0).getTextContent());
                        } catch(final Exception ex) {

                        }
                        PlayList oPlayList = new PlayList(ePlayList.getElementsByTagName("name").item(0).getTextContent(), dt);
                        Collection<Song> pl2Songs = new ArrayList<>();

                        // για όλα τα song
                        NodeList nlSongList = ePlayList.getElementsByTagName("song");
                        for(int j=0; j<nlSongList.getLength(); j++) {
                            Node nSongList = nlSongList.item(j);

                            if (nSongList.getNodeType() == Node.ELEMENT_NODE) {
                                Element eSong = (Element) nSongList;

                                System.out.println("Song.ID: " + eSong.getAttribute("id"));
                                System.out.println("Song.Title: " + eSong.getElementsByTagName("title").item(0).getTextContent());
                                System.out.println("Song.duration: " + eSong.getElementsByTagName("duration").item(0).getTextContent());
                                System.out.println("Song.tracknr: " + eSong.getElementsByTagName("tracknr").item(0).getTextContent());

                                Element eAlbum = (Element) eSong.getElementsByTagName("album").item(0);
                                System.out.println("Album.ID: " + eAlbum.getAttribute("id"));
                                System.out.println("Album.title: " + eAlbum.getElementsByTagName("title").item(0).getTextContent());
                                System.out.println("Album.type: " + eAlbum.getElementsByTagName("type").item(0).getTextContent());
                                System.out.println("Album.disknumber: " + eAlbum.getElementsByTagName("disknumber").item(0).getTextContent());

                                Element eCompany = (Element) eAlbum.getElementsByTagName("company").item(0);
                                System.out.println("company.ID: " + eCompany.getAttribute("id"));
                                System.out.println("company.name: " + eCompany.getElementsByTagName("name").item(0).getTextContent());

                                // βρες ή δημιούργησε company
                                MusicProductionCompany oMusicProductionCompany = CreateCompany(parentComponent, eCompany.getElementsByTagName("name").item(0).getTextContent());

                                Album oAlbum;
                                if(eAlbum.getElementsByTagName("musicgroup").item(0)==null){
                                    Element eArtist = (Element) eAlbum.getElementsByTagName("artist").item(0);
                                    System.out.println("Artist.ID: " + eArtist.getAttribute("id"));
                                    System.out.println("Artist.name: " + eArtist.getElementsByTagName("name").item(0).getTextContent());

                                    Element eMusicGenre = (Element) eAlbum.getElementsByTagName("musicgenre").item(0);
                                    System.out.println("musicgenre.ID: " + eMusicGenre.getAttribute("id"));
                                    System.out.println("musicgenre.name: " + eMusicGenre.getElementsByTagName("name").item(0).getTextContent());
                                    // βρες ή δημιούργησε artist και music genre
                                    Artist oArtist = CreateArtistMusicGenre(parentComponent, eArtist.getElementsByTagName("name").item(0).getTextContent(), eMusicGenre.getElementsByTagName("name").item(0).getTextContent());
                                    // βρες ή δημιούργησε album
                                    Album oAlbum1 = CreateAlbum(parentComponent, eAlbum.getElementsByTagName("title").item(0).getTextContent(), 
                                                               eAlbum.getElementsByTagName("type").item(0).getTextContent(), 
                                                               eAlbum.getElementsByTagName("disknumber").item(0).getTextContent(), 
                                                               oMusicProductionCompany, null, oArtist);
                                    oAlbum = oAlbum1;
                                } else {
                                    Element eMusicGroup = (Element) eAlbum.getElementsByTagName("musicgroup").item(0);
                                    System.out.println("musicgroup.ID: " + eMusicGroup.getAttribute("id"));
                                    System.out.println("musicgroup.name: " + eMusicGroup.getElementsByTagName("name").item(0).getTextContent());
                                    // βρες ή δημιούργησε MusicGroup
                                    MusicGroup oMusicGroup = CreateMusicGroup(parentComponent, eMusicGroup.getElementsByTagName("name").item(0).getTextContent());
                                    // βρες ή δημιούργησε album
                                    Album oAlbum2 = CreateAlbum(parentComponent, eAlbum.getElementsByTagName("title").item(0).getTextContent(), 
                                                               eAlbum.getElementsByTagName("type").item(0).getTextContent(), 
                                                               eAlbum.getElementsByTagName("disknumber").item(0).getTextContent(), 
                                                               oMusicProductionCompany, oMusicGroup, null);
                                    oAlbum = oAlbum2;
                                }
                                // βρες ή δημιούργησε song
                                Song oSong = CreateSong(parentComponent, eSong.getElementsByTagName("title").item(0).getTextContent(), 
                                                        eSong.getElementsByTagName("duration").item(0).getTextContent(), 
                                                        eSong.getElementsByTagName("tracknr").item(0).getTextContent(), oAlbum);
                                pl2Songs.add(oSong);
                            }
                        }
                        System.out.println("ID: " + ePlayList.getAttribute("id"));
                        System.out.println("Name: " + ePlayList.getElementsByTagName("name").item(0).getTextContent());
                        System.out.println("creationdate: " + ePlayList.getElementsByTagName("creationdate").item(0).getTextContent());
                        oPlayList.setSongCollection(pl2Songs);
                        // βρες κι ενημέρωσε ή δημιούργησε playlist
                        CreatePlayList(parentComponent, oPlayList);
                    }
                }

            } catch(ParserConfigurationException | SAXException | IOException ex) {
                ex.printStackTrace();
            } 
        } 
        
    } 
    
}
